﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Management.Automation;
using System.Text;
using System.Web.Services.Protocols;

namespace SPWebShell.cmdlet
{
    [Cmdlet(VerbsCommon.Add, "SPListItem")]
    public class AddListItem : CmdLetBase
    {
        [Parameter(Mandatory = true, HelpMessage = "The title of the List")]
        [ValidateNotNullOrEmpty]
        public string List { get; set; }

        [Parameter(Mandatory = true, HelpMessage = "The absolute URL to the web site where the List is located or any item/view within the List. e.g. http://server/sites/SiteA/doclib1/Allitems.aspx")]
        [ValidateNotNullOrEmpty]
        public string Url { get; set; }


        [Parameter(Mandatory = false, HelpMessage = "A comma separated list of internal names whose values will be applied to the document list item")]
        public string[] Properties { get; set; }
        [Parameter(Mandatory = false, HelpMessage = "A comma separated list of field values specified in the same order as Properties")]
        public string[] Values { get; set; }

        protected override void ProcessRecord()
        {
            try
            {
                WriteVerbose("Start time: " + DateTime.Now.ToString());
                if (this.Properties == null)
                {
                    this.Properties = new string[] { };
                }
                if (this.Values == null)
                {
                    this.Values = new string[] { };
                }
                ISPRepository repos = base.SPRepos;
                SPDoclibLocation location = repos.ResolveDoclibUrlFromPageUrl(this.Url);
                //If a List name has been provided then use this name
                if (!string.IsNullOrEmpty(this.List)) location.DoclibName = this.List;
                entity.SPList listInfo = GetListInfoFromCache(location);
                PurifyFieldNames(listInfo);
                entity.SPListItem newitem=SPRepos.CreateListItem(location,this.Properties, this.Values);
                if (newitem==null)
                {
                    throw new Exception("Unspecified error.Could not create lis item");
                }
                WriteVerbose(string.Format("Item created. ID={0}",newitem.SPID));
                WriteVerbose("Requerying this list to fetch the new item");
                entity.SPListItem[] items = SPRepos.GetListItems(location, newitem.SPID- 1, 1, this.Properties, false, null);
                WriteVerbose("Requery complete");
                if ((items == null) || (items.Count() == 0))
                {
                    WriteVerbose("No items found after requerying");
                    throw new Exception(string.Format("No item with ID:{0} was found", newitem.SPID));
                }
                PSObject o1 = new PSObject();
                entity.SPListItem thisItem = items[0];
                o1.Members.Add(new PSNoteProperty("ID", thisItem.SPID));
                o1.Members.Add(new PSNoteProperty("Name", thisItem.Name));
                foreach (string fieldName in this.Properties)
                {
                    Object fieldValue = thisItem.FieldValues[fieldName];
                    o1.Members.Add(new PSNoteProperty(fieldName, fieldValue));
                }
                WriteObject(o1);
                base.ProcessRecord();
            }
            catch (SoapException ex)
            {
                string message = ex.Detail.OuterXml;
                throw new Exception(message, ex);
            }
            catch (Exception ex)
            {
                WriteError(new ErrorRecord(ex,"", ErrorCategory.NotSpecified, null));
            }
            finally
            {
                WriteVerbose("End time: " + DateTime.Now.ToString());
            }
        }
        /// <summary>
        /// Iterates through all meta-data propery names and trims/converts to right case.
        /// Rationale - to make it easy for user to specify internal names without being too strict
        /// </summary>
        private void PurifyFieldNames(entity.SPList listInfo)
        {
            if (this.Properties == null) return;
            if (this.Properties.Length == 0) return;
            List<string> purifiedpropnames = new List<string>();
            foreach (string propname in this.Properties)
            {
                if (string.IsNullOrEmpty(propname)) throw new Exception("Cannot accept NULL property names");
                string propnametrimmed = propname.Trim();
                entity.SPField thisField = listInfo.Fields.FirstOrDefault(f => (f.InternalName.Equals(propnametrimmed, StringComparison.CurrentCultureIgnoreCase)));
                if (thisField == null) throw new Exception(string.Format("No property found with the name:{0}", propnametrimmed));
                purifiedpropnames.Add(thisField.InternalName);
            }
            this.Properties = purifiedpropnames.ToArray();
        }
        /// <summary>
        /// Retrieves a caches copy of the List meta-data
        /// </summary>
        /// <param name="listlocation"></param>
        /// <returns></returns>
        entity.SPList GetListInfoFromCache(SPDoclibLocation listlocation)
        {
            System.Runtime.Caching.MemoryCache cache = System.Runtime.Caching.MemoryCache.Default;
            System.Security.Cryptography.MD5 md5 = System.Security.Cryptography.MD5.Create();
            string urlpluslistname = listlocation.DoclibName + listlocation.SiteUrl;
            byte[] input = System.Text.Encoding.UTF8.GetBytes(urlpluslistname);
            byte[] bKey = md5.ComputeHash(input);
            string sKey = System.Text.Encoding.UTF8.GetString(bKey);
            entity.SPList listInfo = null;
            if (!cache.Contains(sKey))
            {
                VerboseLogger("List metadata was not found in cache");
                listInfo = base.SPRepos.GetList(listlocation);
                System.Runtime.Caching.CacheItemPolicy pol = new System.Runtime.Caching.CacheItemPolicy();
                pol.SlidingExpiration = new TimeSpan(0, 1, 0);
                pol.Priority = System.Runtime.Caching.CacheItemPriority.Default;
                cache.Add(sKey, listInfo, pol);
            }
            else
            {
                VerboseLogger("Found list metadata in cache");
                listInfo = (entity.SPList)cache[sKey];
            }
            return listInfo;
        }
    }
}
